/*
 * Created by SharpDevelop.
 */
using System;
using System.Collections.Generic;

namespace MiniMIPS
{
	/// <summary>
	/// Description of Instruction.
	/// </summary>
	/// 
	
	[Serializable]
	public class Instruction
	{
		private Operation m_op = new Operation();
		
		public string Comments = "";
		
		public Operation Op
		{
			get
			{
				return m_op;
			}
			set
			{
				if(value.Type != m_op.Type)
				{
					if(value.Type == OpType.JType)
					{
						this.Params[0] = 0;
					}
					else
					{
						this.Params[0] = 1;
					}
					
					for(int j = 1; j < 3; j++)
					{
						this.Params[j] = 0;
					}
				}
				
				m_op = value;
			}
		}
		public List<int> Params;
		
		public string Hex
		{
			get
			{
				UInt16 val = Convert.ToUInt16(this.Binary, 2);
				return String.Format( "{0:X2}", val).PadLeft(4,'0');
			}
		}
		
		public string Binary
		{
			get 
			{
				string str = BinaryString.FromUInt(Op.OpCode,4);
				
				if(Op.Type == OpType.RType)
				{
					str += BinaryString.FromInt(this.Rs,3);
					str += BinaryString.FromInt(this.Rt,3);
					str += BinaryString.FromInt(this.Rd,3);
					str += BinaryString.FromUInt(Op.FuncCode,3);
				}
				else if(Op.Type == OpType.IType)
				{
					str += BinaryString.FromInt(this.Rs,3);
					str += BinaryString.FromInt(this.Rt,3);
					str += BinaryString.FromInt(this.Imm,6);
				}
				else if(Op.Type == OpType.JType)
				{
					str += BinaryString.FromInt(0, 5);
					str += BinaryString.FromInt(this.Addr, 7);
				}
				else
				{
					throw new Exception("Unknown OpType");
				}
				
				if(str.Length != 16)
				{
					throw new Exception("Incorrect Binary Instruction Length");
				}
				
				return str;
			}
		}
		
		public Instruction()
		{
			Op = new Operation();
			Params = new List<int>();
			for(int i = 0; i < 3; i++)
			{
				Params.Add(0);
			}
		}
		
		public Instruction(Operation op, List<int> param)
		{
			Op = op;
			Params = param;
		}
		
		public int Rd
		{
			get 
			{
				if(this.Op.Type != OpType.RType)
				{
					throw new Exception("Got Rd of Non-RType");
				}
				
				return Params[0];
			}
		}
		
		public int Rt
		{
			get 
			{
				if(this.Op.Type == OpType.JType)
				{
					throw new Exception("Got Rt of JType");
				}
				
				if(this.Op.Type == OpType.IType)
				{
					return Params[0];
				}
				else
				{
					return Params[2];
				}
			}
		}
		
		public int Rs
		{
			get
			{
				if(this.Op.Type == OpType.JType)
				{
					throw new Exception("Got Rs of JType");
				}
				
				return Params[1];
			}
		}
		
		public int Imm
		{
			get
			{
				if(this.Op.Type != OpType.IType)
				{
					throw new Exception("Got Imm of Non-IType");
				}
				return Params[2];
			}
			
		}
		
		public int Addr
		{
			get
			{
				if(this.Op.Type != OpType.JType)
				{
					throw new Exception("Got Addr of Non-JType");
				}
				return Int8.Unsigned(Params[0]);
			}
			
		}
		
		public override string ToString()
		{
			string str = this.Op.Name.PadRight(4,' ');
		
			if(this.Op.Type == OpType.RType)
			{
				str += " $" +  Enum.GetName(typeof(RegsD), this.Rd);
				str += ", $" + Enum.GetName(typeof(RegsS), this.Rs);
				str += ", $" + Enum.GetName(typeof(RegsS), this.Rt);
			}
			else if(this.Op.Type == OpType.IType)
			{
				if(this.Op.Func == OpFunc.BranchEq ||
				   this.Op.Func == OpFunc.Store)
				{
					str += " $" + Enum.GetName(typeof(RegsS), this.Rt);
				}
				else
				{
					str += " $" + Enum.GetName(typeof(RegsD), this.Rt);
				}
				
				if(this.Op.Func == OpFunc.Load ||
				   this.Op.Func == OpFunc.Store)
				{
					str += ", " + this.Imm.ToString();
					str += "($" + Enum.GetName(typeof(RegsS), this.Rs) + ")";
				}
				else
				{
					str += ", $" + Enum.GetName(typeof(RegsS), this.Rs);
					str += ", $" + this.Imm.ToString();
				}
			}
			else if(this.Op.Type == OpType.JType)
			{
				str += " " + this.Addr.ToString();
			}
			
			return str;
		}
		
		public string VHDL
		{
			get 
			{
				string hex = this.Hex;
				return "x\"" + Hex.Substring(0,2) + "\",x\"" + Hex.Substring(2,2) + "\"";
			}
		}
		
	}
}
